/*************
author:: libo
license:: MIT
*****************/


#include <tool/libcell_tools_octree.h>

void oc_node_init_from_node(OC_Node*ocn,Node*node)
{
    oc_node_init(ocn);
    if(node==NULL)
    {
        return ;
    }
    Node*nit=node;
    template_v* v=(template_v*)(nit->value);
    ocn->loc_min[0]=v->point[0];  ocn->loc_min[1]=v->point[1]; ocn->loc_min[2]=v->point[2];
    ocn->loc_max[0]=v->point[0];  ocn->loc_max[1]=v->point[1]; ocn->loc_max[2]=v->point[2];
    for(;nit!=NULL;nit=(Node*)(nit->Next))
    {
        v=(template_v*)(nit->value);
        for(int i=0;i<3;i++)
        {
            v->point[i]>ocn->loc_max[i]?(ocn->loc_max[i]=v->point[i]):(v->point[i]<ocn->loc_min[i]?(ocn->loc_min[i]=v->point[i]):(i=i));
        }
    }    
    if(ocn->loc_max[0]-ocn->loc_min[0]<1e-4){ocn->loc_max[0]+=0.0001;ocn->loc_min[0]-=0.0001;}
    if(ocn->loc_max[1]-ocn->loc_min[1]<1e-4){ocn->loc_max[1]+=0.0001;ocn->loc_min[1]-=0.0001;}
    if(ocn->loc_max[2]-ocn->loc_min[2]<1e-4){ocn->loc_max[2]+=0.0001;ocn->loc_min[2]-=0.0001;}

    ocn->value=node_copy(node);
}
void oc_node_free(OC_Node*ocn)
{
    if(ocn==NULL)
    {
        return ;
    } 
    for(int i=0;i<8;i++)
    {
        oc_node_free(ocn->children[i]);
    } 
    free_node(ocn->value);
    free(ocn);
    return;
}

int oc_node_divide_one_leaf(OC_Node* ocn)
{
    if(ocn==NULL)
    {return 0;}
    if(ocn->children[0]!=NULL)
    {
        printf("oc_node_divide_one_leaf is not leaf\n");
        return 0;
    }
    double steps[3]={0,0,0};
    for(int i=0;i<3;i++)
    {
        steps[i]=(ocn->loc_max[i]-ocn->loc_min[i])/2.0;
    }
    if(steps[0]<=0||steps[1]<=0||steps[2]<=0)
    {
        printf("steps is zero\n");
        return 0;
    }
    for(int i=0;i<2;i++)
    {
        for(int j=0;j<2;j++)
        {
            for(int k=0;k<2;k++)
            {
                ocn->children[i*4+j*2+k]=(OC_Node*)malloc(sizeof(OC_Node));
                oc_node_init(ocn->children[i*4+j*2+k]);ocn->i=i;ocn->j=j;ocn->k=k;
                ocn->children[i*4+j*2+k]->depth=ocn->depth+1;
                ocn->children[i*4+j*2+k]->parent=ocn;
                ocn->children[i*4+2*j+k]->loc_min[0]=ocn->loc_min[0]+steps[0]*i;
                ocn->children[i*4+2*j+k]->loc_min[1]=ocn->loc_min[1]+steps[1]*j; 
                ocn->children[i*4+2*j+k]->loc_min[2]=ocn->loc_min[2]+steps[2]*k;
                ocn->children[i*4+2*j+k]->loc_max[0]=ocn->children[i*4+2*j+k]->loc_min[0]+steps[0]; 
                ocn->children[i*4+2*j+k]->loc_max[1]=ocn->children[i*4+2*j+k]->loc_min[1]+steps[1]; 
                ocn->children[i*4+2*j+k]->loc_max[2]=ocn->children[i*4+2*j+k]->loc_min[2]+steps[2]; 
            }
        }
    }
    for(Node* nit=(Node*)(ocn->value);nit!=NULL;nit=(Node*)(nit->Next))
    {
        template_v* vv=(template_v*)(nit->value);
        int i=0,j=0,k=0;
        i=(int)((vv->point[0]-ocn->loc_min[0])/steps[0]);  
        j=(int)((vv->point[1]-ocn->loc_min[1])/steps[1]);
        k=(int)((vv->point[2]-ocn->loc_min[2])/steps[2]);
        i>=2?(i=1):(i<0?(i=0):(i=i));
        j>=2?(j=1):(j<0?(j=0):(j=j));
        k>=2?(k=1):(k<0?(k=0):(k=k));
        
        Node* n=(Node*)(ocn->children[i*4+j*2+k]->value);
        n=node_overlying(n,vv);
        ocn->children[i*4+2*j+k]->value=n; 
    } 
    free_node((Node*)(ocn->value));
    ocn->value=NULL;
    int re=0;
    for(int i=0;i<8;i++)
    {   if(ocn->children[i]->value!=NULL)
        {re++;}
    } 

    return re;
}


int oc_node_vertices_size(OC_Node* ocn)
{
    int re=0;
    if(ocn==NULL)
    {
        return re;
    }
    if(ocn->children[0]==NULL)
    {
        return node_size((Node*)(ocn->value)); 
    } 
    for(int i=0;i<8;i++)
    {
        re+=oc_node_vertices_size(ocn->children[i]);

    }
    return re;
}
int oc_node_voxel_size(OC_Node* ocn)
{
    int re=0;
    if(ocn==NULL)
    {
        return re;
    } 
    if(ocn->children[0]==NULL)
    {
        if(ocn->value!=NULL)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
    for(int i=0;i<8;i++)
    {
        re+=oc_node_voxel_size(ocn->children[i]);
    }
    return re;
}

int oc_node_leaf_size(OC_Node* ocn)
{
    int re=0;
    if(ocn==NULL)
    {
        return re;
    } 

    if(ocn->children[0]==NULL)
    {
        return 1;
    }
    for(int i=0;i<8;i++)
    {
        re+=oc_node_leaf_size(ocn->children[i]);
    } 
    return re;
}

int oc_node_divide_all_leaves(OC_Node* ocn)
{
    int re=0;
    if(ocn==NULL)
    {return re;}
    if(ocn->children[0]==NULL)
    {
        return (ocn->value==NULL?0:oc_node_divide_one_leaf(ocn));
    }
    for(int i=0;i<8;i++)
    {
        re+=oc_node_divide_all_leaves(ocn->children[i]);
    }
    return re;
}

//num>0
    //oc_node_divide_all_leaves1
int oc_node_divide_all_leaves1(OC_Node* ocn,int num)
{
    int re=0;
   if(ocn==NULL)
    {
        return re;
    }
    if(ocn->children[0]==NULL)
    {
        int sum=node_size((Node*)(ocn->value));
        if(sum>num)
        {
            return  oc_node_divide_one_leaf(ocn);

        }
        else if(sum<=num)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
    for(int i=0;i<8;i++)
    {
        re+=oc_node_divide_all_leaves1(ocn->children[i],num);
    }
    return re;
}

typedef struct Libcell_Navigation_Path_Of_OC_Nodes
{   
    OC_Node*ocn;
    int dir;
    Node* path;

}LNPathOC_Nodes;

static inline void lnpocn_init(LNPathOC_Nodes*lnpocn)
{
    lnpocn->ocn=NULL;
    lnpocn->dir=0;
    lnpocn->path=NULL;
}

static LNPathOC_Nodes* get_target_oc_node(LNPathOC_Nodes*lnpocn)
{
    if(lnpocn->path==NULL&&lnpocn->dir==0)
    {
        return lnpocn;
    }
    if(lnpocn->dir==0)
    {
        if(lnpocn->ocn->children[0]==NULL){return lnpocn;}
        //re->path=node_copy((Node*)(lnpocn->path->Next));
        Node* tempn=(Node*)(lnpocn->path);

        lnpocn->path=(Node*)(lnpocn->path->Next);
        lnpocn->path->Prev=NULL;

        int* dir1=(int*)(tempn->value);

        lnpocn->ocn=lnpocn->ocn->children[dir1[0]];

        free(dir1);
        free(tempn);
        return lnpocn;
    }
    if(lnpocn->ocn->parent==NULL){return lnpocn;}

    int dir= (lnpocn->ocn->i*4+lnpocn->ocn->j*2+lnpocn->ocn->k+lnpocn->dir);
    
    lnpocn->dir=dir/8;

    int* dir1=(int*)malloc(sizeof(int));
    *dir1=dir%8;
    lnpocn->path=node_overlying(lnpocn->path,dir1);

    lnpocn->ocn=lnpocn->ocn->parent;


    return get_target_oc_node(lnpocn);
}

OC_Node* get_relative_direction_oc_node(OC_Node* ocn,int x,int y ,int z)
{
    LNPathOC_Nodes*lnpocn=(LNPathOC_Nodes*)malloc(sizeof(LNPathOC_Nodes));
    lnpocn_init(lnpocn);
    lnpocn->dir=4*x+2*y+z;
    lnpocn->ocn=ocn;

    lnpocn=get_target_oc_node(lnpocn);
    free_node_value(lnpocn->path);
    free_node(lnpocn->path);
    OC_Node*re=lnpocn->ocn;
    free(lnpocn);
    return re;
}

// Node* get_neighborhood6_oc_nodes(OC_Node* ocn)
// {



//     return NULL;
// }